/*
 * File: ContactXMLWriter.java
 * Written by Kyle McCready, Viet Le, and David Hudson
 * Started April 11, 2012
 * Stored at http://code.google.com/p/sa-con-man/ on Google code
 * 
 * This program was written as the final project for CSE489 - Zero Defect 
 * Software Design Spring 2012. It follows the specification submitted on 
 * April 11 2012, which can be viewed online at: http://goo.gl/MH0Ol
 * 
 * This file is the XML writer portion of the application, which works in 
 * conjunction with the following files:
 * ContactManager.java
 * ContactFileEncryptor.java
 * ContactXMLParser.java
 */
package contactmanager;

import java.io.*;
import java.util.ArrayList;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
 
import org.w3c.dom.Attr;
import org.w3c.dom.Document;
import org.w3c.dom.Element;

public class ContactXMLWriter
{
    private String OutFileName;
    private Document dom;

    public ContactXMLWriter(String _OutFileName)
    {
        OutFileName = _OutFileName;
    }

    public void ExportContacts(ArrayList<ContactObject> contactList,
                               String password)
    {
        try
        {
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();

            dom = db.newDocument();
            Element root = dom.createElement("ContactIDList");
            dom.appendChild(root);
            for (ContactObject contact : contactList)
            {
                Element contactID = dom.createElement("ContactID");
                root.appendChild(contactID);
                appendNames(contactID, contact.getNameList());
                appendPhoneNumbers(contactID, contact.getPhoneNumberList());
                appendAddresses(contactID, contact.getAddressList());
                appendEmails(contactID, contact.getEmailList());
                appendIMs(contactID, contact.getIMList());
            }
            writeXMLFile(password);
        }

        catch (ParserConfigurationException e)
        {
            System.err.println("Invalid Parser Configuration while writing. " 
                                + e.getMessage());
            e.printStackTrace();
        }
        catch (TransformerException e)
        {
            System.err.println("Could not write to xml file. " + e.getMessage());
            e.printStackTrace();
        }
    }

    private void appendNames(Element el, ArrayList<NameObject> nameList)
        throws ParserConfigurationException
    { 
        for (NameObject name : nameList)
        {
            Element contactName = dom.createElement("ContactName");
            el.appendChild(contactName);
            appendFirstName(contactName, name);
            appendMiddleName(contactName, name);
            appendSirName(contactName, name);
            appendNickName(contactName, name);
        }
    }

    private void appendFirstName(Element el, NameObject name)
        throws ParserConfigurationException
    {
        Element fname = dom.createElement("FirstName");
        fname.appendChild(dom.createTextNode(name.firstName));
        el.appendChild(fname);
    }

    private void appendMiddleName(Element el, NameObject name)
        throws ParserConfigurationException
    {
        Element mname = dom.createElement("MiddleName");
        mname.appendChild(dom.createTextNode(name.middleName));
        el.appendChild(mname);
    }

    private void appendSirName(Element el, NameObject name)
        throws ParserConfigurationException
    {
        Element sname = dom.createElement("SirName");
        sname.appendChild(dom.createTextNode(name.sirName));
        el.appendChild(sname);
    }

    private void appendNickName(Element el, NameObject name)
        throws ParserConfigurationException
    {
        Element nname = dom.createElement("NickName");
        nname.appendChild(dom.createTextNode(name.nickName));
        el.appendChild(nname);
    }

    private void appendPhoneNumbers(Element el, ArrayList<PhoneObject> phoneList)
        throws ParserConfigurationException
    {
        for (PhoneObject phone : phoneList)
        {
            Element contactPhone = dom.createElement("ContactPhone");
            el.appendChild(contactPhone);
            appendAreaCode(contactPhone, phone);
            appendLocalNumber(contactPhone, phone);
       }
    }

    private void appendAreaCode(Element el, PhoneObject phone)
    {
        Element area = dom.createElement("AreaCode");
        area.appendChild(dom.createTextNode(phone.areaCode));
        el.appendChild(area);
    }

    private void appendLocalNumber(Element el, PhoneObject phone)
    {
        Element local = dom.createElement("LocalNumber");
        local.appendChild(dom.createTextNode(phone.localNumber));
        el.appendChild(local);
    }

    private void appendAddresses(Element el, ArrayList<AddressObject> addrList)
        throws ParserConfigurationException
    {
        for (AddressObject addr : addrList)
        {
            Element contactAddr = dom.createElement("ContactAddress");
            el.appendChild(contactAddr);
            appendState(contactAddr, addr);
            appendCity(contactAddr, addr);
            appendZip(contactAddr, addr);
            appendStreet(contactAddr, addr);
        }
    }

    private void appendState(Element el, AddressObject addr)
    {
        Element state = dom.createElement("State");
        state.appendChild(dom.createTextNode(addr.state));
        el.appendChild(state);
    }

    private void appendCity(Element el, AddressObject addr)
    {
        Element city = dom.createElement("City");
        city.appendChild(dom.createTextNode(addr.city));
        el.appendChild(city);
    }

    private void appendZip(Element el, AddressObject addr)
    {
        Element zip = dom.createElement("Zip");
        zip.appendChild(dom.createTextNode(addr.zipCode));
        el.appendChild(zip);
    }

    private void appendStreet(Element el, AddressObject addr)
    {
        Element street = dom.createElement("StreetAddress");
        street.appendChild(dom.createTextNode(addr.streetAddress));
        el.appendChild(street);
    }

    private void appendEmails(Element el, ArrayList<EmailObject> emailList)
        throws ParserConfigurationException
    {
        for (EmailObject email : emailList)
        {
            Element contactEmail = dom.createElement("Email");
            contactEmail.appendChild(dom.createTextNode(email.emailAddress));
            el.appendChild(contactEmail);
        }
    }

    private void appendIMs(Element el, ArrayList<IMObject> imList)
        throws ParserConfigurationException
    {
        for (IMObject im : imList)
        {
            Element contactIM = dom.createElement("ContactIM");
            el.appendChild(contactIM);
            appendProvider(contactIM, im);
            appendUserName(contactIM, im);
       }

    }

    private void appendProvider(Element el, IMObject im)
    {
        Element prov = dom.createElement("Provider");
        prov.appendChild(dom.createTextNode(im.provider));
        el.appendChild(prov);
    }

    private void appendUserName(Element el, IMObject im)
    {
        Element un = dom.createElement("UserName");
        un.appendChild(dom.createTextNode(im.userName));
        el.appendChild(un);
    }


    private void writeXMLFile(String password)
        throws TransformerException
    {
        TransformerFactory tf = TransformerFactory.newInstance();
        Transformer t = tf.newTransformer();
        DOMSource source = new DOMSource(dom);
        //File outFile = new File(OutFileName);
        //StreamResult result = new StreamResult(outFile);
        /* Write to byte array instead, for encrypting */
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        StreamResult result = new StreamResult(baos);
        t.transform(source, result);

        ContactFileEncryptor crypt = new ContactFileEncryptor(password);
        crypt.Encrypt(baos.toByteArray(), OutFileName);
    }
}
